#include <gtest/gtest.h>
#include "buffer/page_allocator.h"
#include <stdexcept>
#include <cstdint>
#include <cstring>

class Unorded_PageAllocatorTest : public ::testing::Test {
protected:
    void SetUp() override {
        pages_count = reinterpret_cast<uint16_t*>(malloc(sizeof(uint16_t)));
        data = reinterpret_cast<char*>(malloc(boltDB::BOLTDB_PAGE_SIZE - boltDB::PAGE_HEADER_SIZE));
        *pages_count = 0;
    }

    void TearDown() override {
        free(pages_count);
        free(data);
    }

    uint16_t* pages_count;
    char* data;
};

TEST_F(Unorded_PageAllocatorTest, ConstructorValidInput) {
    EXPECT_NO_THROW(boltDB::Unorded_PageAllocator allocator(pages_count, data));
}

TEST_F(Unorded_PageAllocatorTest, AllocatePageFreePage) {
    *pages_count = 2;
    uint32_t page_ids[] = {1, 2};
    memcpy(data+sizeof(boltDB::page_id_t), page_ids, sizeof(page_ids));
    
    boltDB::Unorded_PageAllocator allocator(pages_count, data);
    EXPECT_EQ(allocator.AllocatePage(), 2);
    EXPECT_EQ(allocator.AllocatePage(), 1);
    EXPECT_EQ(*pages_count, 0);
}

TEST_F(Unorded_PageAllocatorTest, AllocatePageNoFreePage) {
    *pages_count = 0;
    uint32_t page_ids[] = {1};
    memcpy(data, page_ids, sizeof(page_ids));

    boltDB::Unorded_PageAllocator allocator(pages_count, data);
    EXPECT_EQ(allocator.AllocatePage(), 1);
    EXPECT_EQ(allocator.AllocatePage(), 2);
}

TEST_F(Unorded_PageAllocatorTest, DeAllocatePageValid) {
    *pages_count = 0;
    uint32_t page_ids[] = {1};
    memcpy(data, page_ids, sizeof(page_ids));

    boltDB::Unorded_PageAllocator allocator(pages_count, data);
    EXPECT_TRUE(allocator.DeallocatePage(3));
    EXPECT_EQ(*pages_count, 1);
    EXPECT_EQ(allocator.AllocatePage(), 3);
}

TEST_F(Unorded_PageAllocatorTest, DeAllocatePageFull) {
    *pages_count = boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT;
    uint32_t page_ids[boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT + 1];
    memset(page_ids, 0, sizeof(page_ids));
    memcpy(data, page_ids, sizeof(page_ids));

    boltDB::Unorded_PageAllocator allocator(pages_count, data);
    EXPECT_FALSE(allocator.DeallocatePage(3));
    EXPECT_EQ(*pages_count, boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT);
}

TEST_F(Unorded_PageAllocatorTest, BoundaryConditions) {
    *pages_count = boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT;
    uint32_t page_ids[boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT + 1];
    memset(page_ids, 0, sizeof(page_ids));
    memcpy(data, page_ids, sizeof(page_ids));

    boltDB::Unorded_PageAllocator allocator(pages_count, data);
    for (size_t i = 0; i < boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT; ++i) {
        EXPECT_EQ(allocator.AllocatePage(), 0);
    }
    EXPECT_EQ(*pages_count, 0);

    for (size_t i = 0; i < boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT; ++i) {
        EXPECT_TRUE(allocator.DeallocatePage(static_cast<boltDB::page_id_t>(i + 1)));
    }
    EXPECT_EQ(*pages_count, boltDB::Unorded_PageAllocator::MAX_FREE_PAGE_COUNT);
}